package fr.free.jchecs.swg;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import fr.free.jchecs.core.Game.State;
import fr.free.jchecs.core.Game;

/**
 * 
 * @author Michael Silva
 *
 */


public class Statistics {

	/* List of individual player statistics */ 
	private ArrayList<Player> players; 
	
	/* Stores winner if the match */ 
	private String _winner;
	
	/* Constants for readibility */
	private final int _white = 0;
	private final int _black = 1;


	/**
	 * 
	 * @param wAlg
	 * @param wPly
	 * @param bAlg
	 * @param bPly
	 */
	Statistics(String wAlg, int wPly, String bAlg, int bPly) 
	{
		players = new ArrayList<Player>();
		
			// Add white player
		players.add(new Player(wAlg, wPly, false));
		
			// Add black player
		players.add(new Player(bAlg, bPly, true));
		
	}
	
	
	/**
	 * Static method for writing statistics vectors to a file. 
	 * 
	 * @param pFilepath
	 * @param pStats
	 * @throws IOException
	 */
	public static void writeStatisticsToFile (final String pFilepath, 
			final ArrayList<Statistics> pStats) throws IOException
	{
			// For readability
		boolean append = true;
		
		// File file = new File(pFilepath);
		FileWriter fout = new FileWriter(pFilepath, append); 
		
			// Write statistics to output file
		for (Statistics s : pStats){
			fout.write(s.toString() + "\n");
		}
		
			// Add to blank lines for easier reading
		fout.write(blanks(record (pStats)) + "\n" + blanks("") + "\n");
		
			// Done
		fout.close();
	}
	
	
	/**
	 * Updates the number of moves considered by a player.
	 * 	Should be called after each move.
	 * 
	 * @param color
	 * 					Player color.
	 * @param pSearched
	 * 					Number of moves considered during current turn.
	 */
	public void updateSearchedCount(boolean color, int pSearched)
	{
			// White
		if (color) { players.get(_white).updateSearched(pSearched); }
			// Black
		else { players.get(_black).updateSearched(pSearched); } 
	}
	
	
	/**
	 * Set statistics vector for a single match.
	 * 
	 * @param pGame
	 */
	public void setMatchStatistics(final Game pGame) 
	{
			// For readability
		boolean white = true, black = false;
		
			// Set match time
		setRunningTime(white, pGame.getPlayer(white).getEngine().getElapsedTime());
		setRunningTime(black, pGame.getPlayer(black).getEngine().getElapsedTime());
		
			// Determine winner
		determineWinner(pGame);
	}
	
	
	/**
	 * 
	 * @param _game
	 */
	private void determineWinner (Game _game)
	{
		boolean _white = true, _black = false;
	
		if (_game.getState() == State.WHITE_MATES){ setWinner("White"); }
		else if (_game.getState() == State.BLACK_MATES) { setWinner("Black"); }

		else { 
			// Get board valuations for each player
			int whiteScore = _game.getPlayer(_white).getEngine().getHeuristic().evaluate(_game.getBoard(), _white);
			int blackScore = _game.getPlayer(_white).getEngine().getHeuristic().evaluate(_game.getBoard(), _black);
			
			// Decide the match result
			if (whiteScore > blackScore) { setWinner("Draw - White"); }
			else if (whiteScore < blackScore) { setWinner("Draw - Black"); }
			else if (whiteScore == blackScore) { setWinner("Draw - Split"); }
			
		}
	}
	
	
	/**
	 * Should be called after a match ends.  Sets final match
	 * statistics, including Player._time.
	 * 
	 * @param color
	 * @param pTime
	 */
	private void setRunningTime(boolean color, long pTime)
	{		
			// Assertions for debugging
		assert pTime >= 0;
		
			// White
		if (color) { players.get(_white).setRunningTime(pTime); }
			// Black
		else { players.get(_black).setRunningTime(pTime); }
		
	}
	
	
	/**
	 * Set the winner of the match.
	 * 
	 * @param pWinner
	 */
	private void setWinner(final String pWinner)
	{
		assert pWinner != null;
		_winner = pWinner;
	}
	
	/**
	 * Print statistic vector. Includes two player vectors
	 * and the match winner.
	 * 
	 */
	@Override
	public String toString ()
	{
		StringBuilder str = new StringBuilder();
		
		str.append(players.get(_white).toString());
		str.append(",");
		str.append(players.get(_black).toString());
		str.append(",");
		str.append(_winner);
		
		return str.toString();
	}
	
	
	/**
	 * Dummy function used to add blank lines to output file
	 * 	(This function is bad design.)
	 * 
	 * @param str 
	 * 				Optional string to be added at end of blank line.
	 * 				Usually the match record.
	 */
	private static String blanks (String str)
	{
		String p =",,,,,";
		return (p+","+p+","+str);
	}
	
	
	/**
	 * Produce the score
	 */
	private static String record (final ArrayList<Statistics> pStats)
	{
		assert pStats != null;
		
		int w=0, wd=0, b=0, bd=0;
		for (Statistics st : pStats) {
			if (st._winner == "White") { w++; }			
			else if (st._winner == "Draw - White") { wd++; }
			else if (st._winner == "Black") { b++; }
			else if (st._winner == "Draw - Black") { bd++; }
		}
		
		String result = Integer.toString(w) + " - " +
										Integer.toString(wd) + " - " +
										Integer.toString(b) + " - " +
										Integer.toString(bd);
		
		return result;
	}
	
	
		/**
		 * Private class to store stats for a single player.
		 * Class should not be directly accessible from outside 
		 * of Statistics object.
		 * 
		 * @author Michael
		 *
		 */
		private class Player {
			
			/* Name of algorithm being used */
			private final String _name;
			
			/* Algorith's search depth (i.e., ply) */
			private final int _ply;
			
			/* Total number of moves made during match */
			private int _moves;
			
			/* Cumulative number of nodes search */
			private long _searched;
			
			/* Cumulative time player was thinking */
			private long _time;
			
			/* Average number of nodes searched per second */
			private float _speed;
			
			/* Color. 
			 * 	True is white, False if black */
			@SuppressWarnings("unused")
			private boolean _color;
			
			/**
			 * Initialize a new Player.
			 * 
			 * @param name
			 * @param ply
			 * @param c
			 */
			Player (String name, int ply, boolean c)
			{
				_name = name;
				_ply = ply;
				_color = c;
			}
			
			/**
			 *	Updates cummulative player statistsics.
			 *		Called after a player's move.
			 *
			 * @param pSearched
			 */
			private void updateSearched(int pSearched)
			{
				assert(pSearched) > 0;
				_searched += pSearched;
				_moves++;
			}
			
			/**
			 * Updates final match statistics for the player
			 * 
			 * @param pTime 
			 */
			private void setRunningTime(long pTime)
			{
				_time = pTime;
				calcSpeed();
			}
			
			/**
			 * Calculate number of nodes inspected each second.
			 * 
			 */
			private void calcSpeed() 
			{
				int ms_to_sec = 1000;
				float spd = (float)_searched / ((float)_time / ms_to_sec);
				_speed = spd;
			}
			
			/**
			 * Print statistics for single player.
			 * 
			 */
			@Override
			public String toString() 
			{
				StringBuilder str = new StringBuilder();
				
				str.append(_name + ","); // alg name
				str.append(_ply + ","); // search depth
				str.append(_time + ","); // running time in ms
				str.append(_moves + ","); // total number of moves
				str.append(_searched + ","); // total number of nodes examines
				str.append((long)_speed); // nodes/s
				
				return str.toString();
			}
			
			
		}
	
}
